package org.budo.support.spring.boot.web.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.boot.web.registration.WelcomeFileRegistrationBean;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @author limingwei2
 */
public class WebXmlConfigRegistryBean {
    private static final Logger log = Slf4j.getLogger();

    private ApplicationContext applicationContext;

    public WebXmlConfigRegistryBean(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * registerWebXmlConfig
     */
    public void registerWebXmlConfig() {
        try {
            Document document = this.readWebXmlDocument();
            Map<String, String> filterMap = this.filterMap(document);
            Map<String, String> filterMappingMap = this.filterMappingMap(document);
            this.registerFiltersToApplicationContext(applicationContext, filterMap, filterMappingMap);

            List<String> welcomeFiles = this.welcomeFiles(document);
            this.configWelcomeFiles(applicationContext, welcomeFiles);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    private List<String> welcomeFiles(Document document) {
        Elements welcomeFiles = document.select("welcome-file");
        List<String> list = new ArrayList<String>();
        for (Element welcomeFile : welcomeFiles) {
            list.add(welcomeFile.text());
        }
        return list;
    }

    private void configWelcomeFiles(ApplicationContext applicationContext, List<String> welcomeFiles) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(WelcomeFileRegistrationBean.class);
        beanDefinition.getPropertyValues().add("welcomeFiles", welcomeFiles);

        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
        beanDefinitionRegistry.registerBeanDefinition("welcomeFiles", beanDefinition);
    }

    // public EmbeddedServletContainerFactory servletContainer() {
    // TomcatEmbeddedServletContainerFactory factory = new
    // TomcatEmbeddedServletContainerFactory();
    // TomcatContextCustomizer contextCustomizer = new TomcatContextCustomizer()
    // {
    // public void customize(Context context) {
    // context.addWelcomeFile("/index.htm");
    // }
    // };
    // factory.addContextCustomizers(contextCustomizer);
    //
    // return factory;
    // }

    private void registerFiltersToApplicationContext(ApplicationContext applicationContext, Map<String, String> filterMap, Map<String, String> filterMappingMap) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        for (Entry<String, String> entry : filterMappingMap.entrySet()) {
            String filterName = entry.getKey();
            String filterType = filterMap.get(filterName);

            this.registerToApplicationContext(applicationContext, filterName, filterType);
        }
    }

    private void registerToApplicationContext(ApplicationContext applicationContext, String filterName, String filterType) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        log.info("filterName=" + filterName + ", filterType=" + filterType);

        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(FilterRegistrationBean.class);
        beanDefinition.getPropertyValues().add("filter", Class.forName(filterType).newInstance());

        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
        beanDefinitionRegistry.registerBeanDefinition(filterName, beanDefinition);
    }

    private Map<String, String> filterMappingMap(Document document) {
        Map<String, String> filterMappingMap = new HashMap<String, String>();
        Elements filterMappings = document.select("filter-mapping");

        for (Element filterMapping : filterMappings) {
            String filterName = filterMapping.select("filter-name").text();
            String urlPattern = filterMapping.select("url-pattern").text();
            filterMappingMap.put(filterName, urlPattern);
        }
        return filterMappingMap;
    }

    private Map<String, String> filterMap(Document document) {
        Map<String, String> filterMap = new HashMap<String, String>();
        Elements filters = document.select("filter");

        for (Element filter : filters) {
            Elements select = filter.select("filter-name");
            String filterName = select.text();
            String filterClass = filter.select("filter-class").text();
            filterMap.put(filterName, filterClass);
        }
        return filterMap;
    }

    private Document readWebXmlDocument() throws FileNotFoundException, IOException {
        String path = Thread.currentThread().getContextClassLoader().getResource("").toString();
        path = path.replace('/', '\\'); // 将/换成\

        path = path.replace("file:", ""); // 去掉file:

        path = path.replace("classes\\", ""); // 去掉class\

        path = path.substring(1); // 去掉第一个\,如 \D:\JavaWeb...

        path += "classes" + File.separator + "WEB-INF" + File.separator + "web.xml";

        path = path.replace("\\target\\test-classes\\", "\\target\\classes\\");

        log.info("path=" + path);

        File file = new File(path);
        if (file.exists() && file.isFile()) {
            return Jsoup.parse(new FileInputStream(path), "UTF-8", "");
        } else {
            log.error("file not exists, " + path + "=" + path);
            return new Document(path);
        }
    }
}